import GObject from 'gi://GObject';
import Graphene from 'gi://Graphene';
import Meta from 'gi://Meta';
-import Shell from 'gi://Shell';
import St from 'gi://St';
-import * as Animation from './animation.js';
-import {AppMenu} from './appMenu.js';
import * as Config from '../misc/config.js';
import * as CtrlAltTab from './ctrlAltTab.js';
import * as DND from './dnd.js';
-import * as Overview from './overview.js';
import * as PopupMenu from './popupMenu.js';
import * as PanelMenu from './panelMenu.js';
import {QuickSettingsMenu, SystemIndicator} from './quickSettings.js';
import {DwellClickIndicator} from './status/dwellClick.js';
import {ScreenRecordingIndicator, ScreenSharingIndicator} from './status/remoteAccess.js';
-const PANEL_ICON_SIZE = 16;
-const APP_MENU_ICON_MARGIN = 0;
-
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const N_QUICK_SETTINGS_COLUMNS = 2;
const INACTIVE_WORKSPACE_DOT_SCALE = 0.75;
-/**
- * AppMenuButton:
- *
- * This class manages the "application menu" component. It tracks the
- * currently focused application. However, when an app is launched,
- * this menu also handles startup notification for it. So when we
- * have an active startup notification, we switch modes to display that.
- */
-const AppMenuButton = GObject.registerClass({
- Signals: {'changed': {}},
-}, class AppMenuButton extends PanelMenu.Button {
- _init(panel) {
- super._init(0.0, null, true);
-
- this.accessible_role = Atk.Role.MENU;
-
- this._startingApps = [];
-
- this._menuManager = panel.menuManager;
- this._targetApp = null;
-
- let bin = new St.Bin({name: 'appMenu'});
- this.add_child(bin);
-
- this.bind_property('reactive', this, 'can-focus', 0);
- this.reactive = false;
-
- this._container = new St.BoxLayout({style_class: 'panel-status-menu-box'});
- bin.set_child(this._container);
-
- let textureCache = St.TextureCache.get_default();
- textureCache.connect('icon-theme-changed',
- this._onIconThemeChanged.bind(this));
-
- let iconEffect = new Clutter.DesaturateEffect();
- this._iconBox = new St.Bin({
- style_class: 'app-menu-icon',
- y_align: Clutter.ActorAlign.CENTER,
- });
- this._iconBox.add_effect(iconEffect);
- this._container.add_child(this._iconBox);
-
- this._iconBox.connect('style-changed', () => {
- let themeNode = this._iconBox.get_theme_node();
- iconEffect.enabled = themeNode.get_icon_style() === St.IconStyle.SYMBOLIC;
- });
-
- this._label = new St.Label({
- y_expand: true,
- y_align: Clutter.ActorAlign.CENTER,
- });
- this._container.add_child(this._label);
-
- this._visible = !Main.overview.visible;
- if (!this._visible)
- this.hide();
- Main.overview.connectObject(
- 'hiding', this._sync.bind(this),
- 'showing', this._sync.bind(this), this);
-
- this._spinner = new Animation.Spinner(PANEL_ICON_SIZE, {
- animate: true,
- hideOnStop: true,
- });
- this._container.add_child(this._spinner);
-
- let menu = new AppMenu(this);
- this.setMenu(menu);
- this._menuManager.addMenu(menu);
-
- Shell.WindowTracker.get_default().connectObject('notify::focus-app',
- this._focusAppChanged.bind(this), this);
- Shell.AppSystem.get_default().connectObject('app-state-changed',
- this._onAppStateChanged.bind(this), this);
- global.window_manager.connectObject('switch-workspace',
- this._sync.bind(this), this);
-
- this._sync();
- }
-
- fadeIn() {
- if (this._visible)
- return;
-
- this._visible = true;
- this.reactive = true;
- this.remove_all_transitions();
- this.ease({
- opacity: 255,
- duration: Overview.ANIMATION_TIME,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- });
- }
-
- fadeOut() {
- if (!this._visible)
- return;
-
- this._visible = false;
- this.reactive = false;
- this.remove_all_transitions();
- this.ease({
- opacity: 0,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- duration: Overview.ANIMATION_TIME,
- });
- }
-
- _syncIcon(app) {
- const icon = app.create_icon_texture(PANEL_ICON_SIZE - APP_MENU_ICON_MARGIN);
- this._iconBox.set_child(icon);
- }
-
- _onIconThemeChanged() {
- if (this._iconBox.child == null)
- return;
-
- if (this._targetApp)
- this._syncIcon(this._targetApp);
- }
-
- stopAnimation() {
- this._spinner.stop();
- }
-
- startAnimation() {
- this._spinner.play();
- }
-
- _onAppStateChanged(appSys, app) {
- let state = app.state;
- if (state !== Shell.AppState.STARTING)
- this._startingApps = this._startingApps.filter(a => a !== app);
- else if (state === Shell.AppState.STARTING)
- this._startingApps.push(app);
- // For now just resync on all running state changes; this is mainly to handle
- // cases where the focused window's application changes without the focus
- // changing. An example case is how we map OpenOffice.org based on the window
- // title which is a dynamic property.
- this._sync();
- }
-
- _focusAppChanged() {
- let tracker = Shell.WindowTracker.get_default();
- let focusedApp = tracker.focus_app;
- if (!focusedApp) {
- // If the app has just lost focus to the panel, pretend
- // nothing happened; otherwise you can't keynav to the
- // app menu.
- if (global.stage.key_focus != null)
- return;
- }
- this._sync();
- }
-
- _findTargetApp() {
- let workspaceManager = global.workspace_manager;
- let workspace = workspaceManager.get_active_workspace();
- let tracker = Shell.WindowTracker.get_default();
- let focusedApp = tracker.focus_app;
- if (focusedApp && focusedApp.is_on_workspace(workspace))
- return focusedApp;
-
- for (let i = 0; i < this._startingApps.length; i++) {
- if (this._startingApps[i].is_on_workspace(workspace))
- return this._startingApps[i];
- }
-
- return null;
- }
-
- _sync() {
- let targetApp = this._findTargetApp();
-
- if (this._targetApp !== targetApp) {
- this._targetApp?.disconnectObject(this);
-
- this._targetApp = targetApp;
-
- if (this._targetApp) {
- this._targetApp.connectObject('notify::busy', this._sync.bind(this), this);
- this._label.set_text(this._targetApp.get_name());
- this.set_accessible_name(this._targetApp.get_name());
-
- this._syncIcon(this._targetApp);
- }
- }
-
- let visible = this._targetApp != null && !Main.overview.visibleTarget;
- if (visible)
- this.fadeIn();
- else
- this.fadeOut();
-
- let isBusy = this._targetApp != null &&
- (this._targetApp.get_state() === Shell.AppState.STARTING ||
- this._targetApp.get_busy());
- if (isBusy)
- this.startAnimation();
- else
- this.stopAnimation();
-
- this.reactive = visible && !isBusy;
-
- this.menu.setApp(this._targetApp);
- this.emit('changed');
- }
-});
-
const WorkspaceDot = GObject.registerClass({
Properties: {
'expansion': GObject.ParamSpec.double('expansion', null, null,
const PANEL_ITEM_IMPLEMENTATIONS = {
'activities': ActivitiesButton,
- 'appMenu': AppMenuButton,
'quickSettings': QuickSettings,
'dateMenu': DateMenuButton,
'a11y': ATIndicator,